package ptij.ADSL.pn.ATUC;

import ptolemy.kernel.*;
import ptolemy.kernel.util.*;
import ptolemy.data.*;
import ptolemy.data.expr.*;
import ptolemy.actor.*;

import ptij.ADSL.common.MuxDataFrame;

public class InterleaverTx extends TypedAtomicActor {

    /* input Ports */
    public TypedIOPort input;
    
    /* output Ports */
    public TypedIOPort output;

    public Parameter AS0Bytes;
    public Parameter LS0Bytes;
    
    public Parameter AS0Map;
    public Parameter LS0Map;
    
    public Parameter S;    
    public Parameter parityInter;
    
    public Parameter D;

    public InterleaverTx(TypedCompositeActor container, String name) throws
        NameDuplicationException, IllegalActionException {
        super(container, name);

	input = new TypedIOPort(this, "input", true, false);
	input.setTypeEquals(ObjectToken.class);
	
	output = new TypedIOPort(this, "output", false, true);
	output.setTypeEquals(ObjectToken.class);

	AS0Bytes = new Parameter(this, "AS0Bytes", new IntToken(8));
	LS0Bytes = new Parameter(this, "LS0Bytes", new IntToken(8));
	
	AS0Map = new Parameter(this, "AS0Map", new BooleanToken(false));
	LS0Map = new Parameter(this, "LS0Map", new BooleanToken(false));
	
	S = new Parameter(this, "S", new IntToken(1));
	
	parityInter = new Parameter(this, "parityInter", new IntToken(2));
	
	D = new Parameter(this, "D", new IntToken(2));
    }
    
    public void fire() throws IllegalActionException {    

        int j;
 	ObjectToken ot = (ObjectToken) input.get(0);
	int [] d = (int []) ot.getValue();
		
	int index = 0;
	if(_isEven) {
	    index = 1;
	}
	for(j = 0;j < d.length;j++) {
	    _buffer[(_writePos + index + (_D - 1) * index) % _bufSize] = d[j];
	    index++;
	}
        
	if(_isEven) {
	    _readPos = (_readPos + 1) % _bufSize;
	}

	int out[] = new int[_nnInter];
        for(j = 0;j < _nnInter;j++) {
	    out[j] = _buffer[_readPos];
	    _readPos = (_readPos + 1) % _bufSize;
	}
	_writePos = _readPos;
	    	
	output.broadcast(new ObjectToken(out));
    }

    public void initialize() throws IllegalActionException {
        super.initialize();
	
	IntToken it = (IntToken) AS0Bytes.getToken();
	_AS0Bytes = it.intValue();
	
	it = (IntToken) LS0Bytes.getToken();
	_LS0Bytes = it.intValue();
	
	BooleanToken _bt = (BooleanToken) AS0Map.getToken();
	_AS0Map = _bt.booleanValue();
	
	_bt = (BooleanToken) LS0Map.getToken();
	_LS0Map = _bt.booleanValue();
	
	it = (IntToken) S.getToken();
	_S = it.intValue();
	
	_kkInter = 1;
	if(_AS0Map) {
	    _kkInter += _AS0Bytes;
	}
	
	if(_LS0Map) {
	    _kkInter += _LS0Bytes;
	}
	
	if(_AS0Map) {
 	    _kkInter += 1;
	}	
	if(_AS0Map || _LS0Map) {
	    _kkInter += 1;
	}
        _kkInter *= _S;
		
	it = (IntToken) parityInter.getToken();
        _nnInter = _kkInter + it.intValue();
	
	it = (IntToken) D.getToken();
	_D = it.intValue();
		
	_checkParameters();
	
	if(_nnInter % 2 == 0) {
	    _isEven = true;
      	    _bufSize = _nnInter + 1 + (_D - 1) * (_nnInter + 1);
	}
	else {
	    _isEven = false;
	    _bufSize = _D * _nnInter;
	}
	_buffer = new int[_bufSize];
	_readPos = 0;
	_writePos = 0;
    }

    private void _checkParameters() throws IllegalActionException {

        if(_AS0Bytes < 0) {
	    throw new IllegalActionException(this, "AS0 Bytes < 0");	
	}
	
	if(_LS0Bytes < 0) {
	    throw new IllegalActionException(this, "LS0 Bytes < 0");
	}
	
	if(_S != 1 && _S != 2 && _S != 4 && _S != 8 && _S != 16) {
	    throw new IllegalActionException(this, "\nInvalid value(" +
	        _S + ") for parameter S");
	}	

	if(_nnInter > 255) {
	    throw new IllegalActionException(this, "Maximum Interleaved codeword"+
	        " size is 255");
	}
        int par = _nnInter - _kkInter;
	if((par < 0) || (par > 16) || (par % 2 != 0)) {
	    throw new IllegalActionException(this, "Invalid number(" + par +
	        ") of interleaved parity bytes");
	}
        if(par % _S != 0) {
	    throw new IllegalActionException(this, "Interleaved parity " +
	        " bytes should be an integer multiple of S!");
	}    

	if((_D != 1) && (_D != 2) && (_D != 4) && (_D != 8) && (_D != 16) &&
	   (_D != 32) && (_D != 64)) {
	    throw new IllegalActionException(this, "Invalid interleaved depth");
	}
    } 
    
    private int _buffer[];
    private int _bufSize;
    private int _readPos;
    private int _writePos;
    private boolean _isEven;

    private int _AS0Bytes;
    private int _LS0Bytes;
    private boolean _AS0Map;
    private boolean _LS0Map;
    private int _S, _D;
    
    private int _nnInter, _kkInter;    
}
